import ij.IJ;
import ij.ImagePlus;
import ij.gui.NewImage;
import ij.plugin.PlugIn;
import ij.process.ImageProcessor;


import java.awt.Color;


import java.awt.Font;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.PrintStream;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.InputMismatchException;
import java.util.Locale;
import java.util.Random;
import java.util.Scanner;

import pluginUtils.Doublet;
import pluginUtils.Punkt;
import pluginUtils.Quadrilatere;
import pluginUtils.Transformation;


// Read a datafile and a image file, compute the distorted image 
// for anamorphosis inside a cylinder


public class AnamorphCylinder extends ImagePlus implements PlugIn{
	{Locale.setDefault(Locale.US); }
	
	private Random gene=new Random();
	
	// Controlling the size of the distorted image
	private double scaleb=2; 
	// the steps for scanning each square
	private double mstepx=0.125,mstepy=0.125;
	
	private ImageProcessor primeIP; 
	private int X=2800,Y=2100; // image size
	
	private ImagePlus inverted;  
	private ImageProcessor inv_ip;  
	
	private double radcyl=0;
	
	
	
	// For the scaling of the image
	private double x0=0,x1=1,y0=0,y1=1;
	private double acoef,ccoef,vcoef,wcoef; 
	
	private PrintStream debug; 


	public void setCoefs(){
		acoef=X/(x1-x0); 
		vcoef=Y/(y1-y0); 
		ccoef=-acoef*x0; 
		wcoef=-vcoef*y0; 
	}
	
	
	
	
	public void setBounds(double xl,double xu,double yl,double yu){
		x0=xl; 
		x1=xu;
		y0=yl;
		y1=yu;
		 
	}
	
	private void drawPoint(Punkt p){
		double xCenter=p.getY(); 
		double yCenter=p.getZ(); 
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		primeIP.fillOval(intX-10,intY-10,20,20);
	}
	private void drawPoint(Punkt p,ImageProcessor toto){
		double xCenter=p.getY(); 
		double yCenter=p.getZ(); 
		int intX=(int)(xCenter*acoef+ccoef);
		int intY=(int)(yCenter*vcoef+wcoef);
		System.out.println(intX+" ** "+intY);
		toto.fillOval(intX-5,intY-5,10,10);
	}
	
	private void drawLine(double xd,double yd,double xf,double yf,ImageProcessor toto){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		//System.out.println("---->"+XD+" "+YD+" "+XF+" "+YF); 
		toto.drawLine(XD,YD,XF,YF); 
	}
	
	
	private void drawLine(Punkt A,Punkt B,ImageProcessor toto){
		drawLine(A.getY(),A.getZ(),B.getY(),B.getZ(),toto); 
	}

	
	private void drawLine(double xd,double yd,double xf,double yf){
		int XD=(int)(xd*acoef+ccoef); 
		int YD=(int)(yd*vcoef+wcoef); 
		int XF=(int)(xf*acoef+ccoef); 
		int YF=(int)(yf*vcoef+wcoef);
		//System.out.println("---->"+XD+" "+YD+" "+XF+" "+YF); 
		primeIP.drawLine(XD,YD,XF,YF); 
	}
	
	private void drawLine(Punkt A,Punkt B){
		drawLine(A.getY(),A.getZ(),B.getY(),B.getZ()); 
	}
	
	private boolean tropLoin(Punkt A,Punkt B){
		double d=Math.abs(A.getY()-B.getY());
		if(d>Math.PI*radcyl) return true; 
		return false; 
		
	}
	
	private boolean isOK(Punkt A,Punkt B,Punkt C,Punkt D){
		if (tropLoin(A,B)) return false;
		if (tropLoin(C,D)) return false;
		if (tropLoin(A,C)) return false;
		if (tropLoin(B,D)) return false;
		return true; 
	}
	
	
	
	public void run(String arg) {
		System.out.println("go");
		
	
		ArrayList<Quadrilatere> quad=new ArrayList<Quadrilatere>(); 
		// Import original image
		// Location of the source image
		ImagePlus imp = IJ.openImage("/tmp/gilles.png");
		//ImagePlus imp = IJ.openImage("F:/Povray/anamorphoses/earthNasa2.jpg");
		//ImagePlus imp=IJ.openImage("C:/Documents and Settings/Francesco/Mes documents/Mes images/The_Ambassadors.png");

		imp.setTitle("PrimeIP");
		
	
		System.out.println(imp); 
		primeIP=imp.getProcessor();
		X=primeIP.getWidth(); 
		Y=primeIP.getHeight(); 
		
        setStack(null, imp.getStack());
        setCalibration(imp.getCalibration()); 
		
        	show();
		
		
		

		

		
		primeIP.setFont(new Font(null,Font.ITALIC,60));
		primeIP.setColor(Color.RED);
		primeIP.setLineWidth(10); 
	
		setProcessor("Verifying", primeIP);
		
		
		 HashMap<Doublet,Punkt> lesPoints=new HashMap<Doublet,Punkt>();
		 HashMap<Doublet,Quadrilatere>lesCarres =new HashMap<Doublet,Quadrilatere>();
		 HashMap<Doublet,Quadrilatere>QuadrillageDeBase =new HashMap<Doublet,Quadrilatere>();
		 String ligne="rien"; 
		 double hE=0;
		 double incr=0; 
		 double hautCyl=0;
		 
		try{
			// Open the data file and read the data
			//debug=new PrintStream("/tmp/debug.txt");
			//File source = new File("/tmp/quadri.txt");
			File source=new File("/tmp/quadri.txt");
			
			
			
			
			 BufferedReader in = new BufferedReader(new FileReader(source));
             ligne = in.readLine();
             Scanner rl=new Scanner(ligne); 
             rl.useLocale(Locale.US);
             hE=rl.nextDouble(); 
             incr=rl.nextDouble(); 
             ligne=in.readLine();
             rl=new Scanner(ligne); 
             rl.useLocale(Locale.US);
             hautCyl=rl.nextDouble(); 
             radcyl=rl.nextDouble();
             System.out.println(hautCyl+" "+radcyl); 
             
          // create a new image of size (2h)*(2piR)
 			inverted = NewImage.createRGBImage ("Inverted image",(int)(1000*2*Math.PI*radcyl/hautCyl),2000, 1, NewImage.FILL_BLACK);
   
             
 			inv_ip = inverted.getProcessor();
 			
 			for(int i=0;i<inverted.getWidth();i++)
 				for(int j=0;j<inverted.getHeight();j++)
 					inv_ip.putPixel(i,j,0xFFFFFF); 
             
             
             ligne=in.readLine();
             while(true){
            	 System.out.println(ligne); 
            	  rl=new Scanner(ligne); 
                 rl.useLocale(Locale.US);
                 int a= rl.nextInt(); 
                 int b=rl.nextInt(); 
                 double c=rl.nextDouble(); 
                 double d=rl.nextDouble(); 
                 lesPoints.put(new Doublet(a,b),new Punkt(a,b,c,d)); 
                 ligne=in.readLine();
                 
             }
             
		}
		
		
		catch(InputMismatchException e){System.out.println("File read "+ligne);}
		catch(Exception e){System.out.println(e);   } // end of file reading in Windows
	
		double minx=10000; 
		double miny=10000; 
		double maxx=-10000; 
		double maxy=-10000; 
		int mina=10000; 
		int maxa=-10000; 
		int minb=10000; 
		int maxb=-10000; 
		// points are all read, build the quadrilaterals
		
		
		
		for(Doublet d:lesPoints.keySet()){
			int a=d.getA(); 
			int b=d.getB();
			
			
			// If the point is the upper left corner of a elementary square
			if (lesPoints.containsKey(new Doublet(a,b+1))&& // B
					lesPoints.containsKey(new Doublet(a+1,b))&& //C 
					lesPoints.containsKey(new Doublet(a+1,b+1))) //D
			{
				System.out.println("un carre avec : ("+a+" "+b+"),("+a+","+(b+1)+"),("+(a+1)+","+(b+1)+"),("+(a+1)+","+b+")");
				
				if(a<mina) mina=a; 
				if(a>maxa) maxa=a; 
				if(b<minb) minb=b; 
				if(b>maxb) maxb=b; 
			
				Punkt A=lesPoints.get(new Doublet(a,b)); 
				double vx=A.getY(); 
				double vy=A.getZ(); 
				if(vx<minx) minx=vx; 
				if(vy<miny) miny=vy; 
				if(vx>maxx) maxx=vx; 
				if(vy>maxy) maxy=vy;
				A=new Punkt(0,0,vx,vy); 
				
				Punkt B=lesPoints.get(new Doublet(a,b+1)); 
				vx=B.getY(); 
				vy=B.getZ(); 
				if(vx<minx) minx=vx; 
				if(vy<miny) miny=vy; 
				if(vx>maxx) maxx=vx; 
				if(vy>maxy) maxy=vy;
				B=new Punkt(0,0,vx,vy); 
				Punkt C=lesPoints.get(new Doublet(a+1,b));
				vx=C.getY(); 
				vy=C.getZ(); 
				if(vx<minx) minx=vx; 
				if(vy<miny) miny=vy; 
				if(vx>maxx) maxx=vx; 
				if(vy>maxy) maxy=vy;
				C=new Punkt(0,0,vx,vy);
				
				
				
				Punkt D=lesPoints.get(new Doublet(a+1,b+1));
				vx=D.getY(); 
				vy=D.getZ(); 
				if(vx<minx) minx=vx; 
				if(vy<miny) miny=vy; 
				if(vx>maxx) maxx=vx; 
				if(vy>maxy) maxy=vy;
				D=new Punkt(0,0,vx,vy); 
				// TODO : ne sauver ce quadrilatere que si ses cotes ne franchissent pas la frontiere
				/* if(isOK(A,B,C,D))
					lesCarres.put(d,new Quadrilatere(A,B,C,D));
				*/
				
				
				if(!isOK(A, B, C, D)){
				if(tropLoin(A,B))
				{
					if(A.getY()<0) B.setY(B.getY()-2*Math.PI*radcyl); 
					else A.setY(A.getY()-2*Math.PI*radcyl);
				}
				if(tropLoin(A,C))
				{
					if(A.getY()<0) C.setY(C.getY()-2*Math.PI*radcyl); 
					else A.setY(A.getY()-2*Math.PI*radcyl);
				}
				if(tropLoin(B,D))
				{
					if(B.getY()<0) D.setY(D.getY()-2*Math.PI*radcyl); 
					else B.setY(B.getY()-2*Math.PI*radcyl);
				}
				if(tropLoin(C,D))
				{
					if(C.getY()<0) D.setY(D.getY()-2*Math.PI*radcyl); 
					else C.setY(C.getY()-2*Math.PI*radcyl);
				}
				
				}
				
				lesCarres.put(d,new Quadrilatere(A,B,C,D));
			}
			
		
			
		}// for Doublet 
		System.out.println("Bornes de l'image transformee : "+minx+" "+maxx+" "+miny+" "+maxy); 
		
		//Uncomment the following if you want to see the squares on the original image
		
		//setBounds(minx,maxx,miny,maxy);
		inv_ip.setLineWidth(1); 
		inv_ip.setColor(Color.BLACK); 
		show();
		
		
		int nbCarrY=(int)(hE/incr);  //maxa-mina;
		int nbCarrX=(int)(hE/incr);  //maxb-minb;
		
		double stepy=primeIP.getWidth()/(nbCarrY+0.0);
		double stepx=primeIP.getHeight()/(nbCarrX+0.0);
		int stepyInt=primeIP.getWidth()/nbCarrY;
		int stepxInt=primeIP.getHeight()/nbCarrX;
		System.out.println(hE+" "+incr+" "+nbCarrY+" "+stepx+" "+stepy); 


		
		
		setBounds(-Math.PI*radcyl,Math.PI*radcyl,-hautCyl,hautCyl); 
		System.out.println("bornes theoriques "+x0+" "+x1+" "+y0+" "+y1);
		//debug
		Y=2000; 
		X=(int)(2*1000*Math.PI*radcyl/hautCyl);
		
		
		setCoefs(); 
		
	
		
		
		int tlast=-1; 
		
		System.out.println(mina+" "+maxa+" "+minb+" "+maxb); 
		
		try{
			
		//debug=new PrintStream("/tmp/debug.txt"); 
		for(int xa=mina; xa<=maxa;xa++)
			for(int xb=minb;xb<=maxb;xb++){
				if(lesCarres.containsKey(new Doublet(xa,xb))){
					// Compute the quadrilateral
					
					Punkt A=new Punkt(xa,xb,xa*stepy,xb*stepx); 
					Punkt B=new Punkt(xa+1,xb,(xa+1)*stepy,(xb)*stepx);
					Punkt C=new Punkt(xa,xb+1,(xa)*stepy,(xb+1)*stepx);
					Punkt D=new Punkt(xa+1,xb+1,(xa+1)*stepy,(xb+1)*stepx);
				
					
					Quadrilatere nouvo=new Quadrilatere(A,B,C,D);
					QuadrillageDeBase.put(new Doublet(xa,xb), nouvo);
					
					
					// Setting the transformation from a square to its quadrilateral
					Transformation t=new Transformation(nouvo,lesCarres.get(new Doublet(xa,xb)));
					
						quad.add(lesCarres.get(new Doublet(xa,xb))); 
					
					
					
					
					
					// For each pixel in the original image, compute its image
					// Parameter : mstepx, mstepy : distance between two adjacent points 
					
					double x=xa*stepy; 
					while(x<=(xa+1)*stepy){
						
						double y=xb*stepx; 
						while(y<=(xb+1)*stepx){
							// transform one point
							//if(x*x+y*y<=2000*2000){
							Punkt orig=new Punkt(0,0,x,y); 
							Punkt dest=t.transforme(orig);
							int col=primeIP.getPixel((int)x,(int)y);
							inv_ip.putPixel(X-(int)(acoef*dest.getY()+ccoef),Y-(int)(vcoef*dest.getZ()+wcoef),col);
							//inv_ip.putPixel(X-(int)(acoef*dest.getY()+ccoef),(int)(vcoef*dest.getZ()+wcoef),col);
							//}	
							y=y+mstepy;
						}
						x=x+mstepx;
							}
				}
					
					
					 
			}// quadrillage image (boucle mina minb)
		}
		catch(Exception e){System.out.println(e);} 
		
		show();
		
		System.out.println("Bornes de l'image transformee : "+minx+" "+maxx+" "+miny+" "+maxy); 
		
		
		
		/*
		for(int i=0;i<quad.size();i++){
		//System.out.println(memo[j].verif()+" \n***************\n");
		Quadrilatere q=quad.get(i);
		drawLine(q.getUl(),q.getUr(),inv_ip);
		drawLine(q.getUl(),q.getLl(),inv_ip); 
		drawLine(q.getLl(),q.getLr(),inv_ip); 
		drawLine(q.getLr(),q.getUr(),inv_ip);		
		drawPoint(q.getUl(),inv_ip);
		drawPoint(q.getUr(),inv_ip);
		drawPoint(q.getLr(),inv_ip);
		drawPoint(q.getLl(),inv_ip);
		//System.out.println(q+"\n");
	
	}
	*/
		inverted.show();
		
		System.out.println("end !");
	
		
		}
	}	

	